/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is COID http service.
*
* The Initial Developer of the Original Code is
* PosAm.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brano Kemen
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */


#include "httpserv.h"
#include "coid/comm/binstream/filestream.h"
#include "coid/comm/binstream/txtstreamhtml.h"
#include "coid/comm/dir.h"

#include <time.h>

namespace coid {


#define MAX_TIMEOUT     15*1000

#define SERVER_STRING   "coid/httpserv 0.2"

    MIME_REG    HttpServ::_mimereg;
    charstr     HttpServ::_srvaddr;

    HttpServ::t_frhash  HttpServ::_hash;
    comm_mutex          HttpServ::_hashmutex;

#ifdef _LOG_REQUESTS
    comm_mutex          HttpServ::_logmutex;
    bofstream           HttpServ::_logfile;
#endif

    ////////////////////////////////////////////////////////////////////////////////
    HttpServ::HttpServ()
    {
    }

    ////////////////////////////////////////////////////////////////////////////////
    net_fnc_s opcd HttpServ::accept_net_connections( netSocket* socket, uint port )
    {
        try {
            socket->setNoDelay( false );
            socket->setLinger( true, 10 );
            netstreamtcp nrs(*socket);
            return process( nrs );
        }
        catch( opcd cd )
        {
            return cd;
        }
    }

    ////////////////////////////////////////////////////////////////////////////////
    opcd HttpServ::process( netstreamtcp& net )
    {
        binstream* log = 0;

#ifdef _LOG_REQUESTS
        _log.bind(_logb);
        log = &_log;
#endif

        MsecTimer sampler;
        sampler.reset();

        while( net.is_open() && !_cmdin->is_exit_signalled() )
        {
            _http.reset_read();
            _http.bind(net);
            _http.set_listener(true);

            net.set_timeout(500);

            opcd e = net.is_open() ? _http.read_header() : ersNO_MORE;

            sampler.time();

            if(e == ersNO_MORE) break;
            if(e)
            {
                _http.acknowledge(true);

                if(net.is_open())
                    send_error( "400 Bad Request" );

                continue;
            }

            const httpstream::header& hdr = _http.get_header();

#ifdef _LOG_REQUESTS
            {
                MXGUARD(_logmutex);
                if( !_logfile.is_open() )
                {
                    _logfile.open( ".logreq" );
                    if( _logfile.is_open() ) {
                        *log << "\n\n================================================================================\n"
                            << timet::current() << "\n\n";
                        *log << BINSTREAM_FLUSH;
                    }
                }
            }
#endif

            if( e || !net.is_open() )
                break;

            _cwd.resize( _cwdlen );
            _cwd += hdr._relpath;

            static token _GET("GET");
            static token _POST("POST");
            static token _SOAP("/soap/xmlagw");

            if( hdr._method == _GET  ||  hdr._method == _POST )
            {
                static token Qcoid = "coid-";

                token query = hdr._query;

                if( query.consume(Qcoid) )      //coid specific requests
                {
                    if( !_tun.is_connected() )
                        _tun.connect( _srvaddr.ptr() );

                    netAddress addr;
                    net.get_remote_address( &addr );

                    if( _tun.is_connected() )
                    {
                        //httpstreamcoid net( _hdr, _caxe );
                        _http.set_response( "200 OK" );

                        e = _tun.process_local_call( _http, query, addr );

                        sampler.time();

                        if( !e  &&  !hdr._bclose )
                        {
                            uint n = MAX_TIMEOUT / 2000;
                            while( n-->0 && !e && !_cmdin->is_exit_signalled() ) {
                                e = net.wait_read(2000);
                            }
                            if(!e)  continue;
                        }
                    }
                    else
                    {
                        send_error( "503 Service Unavailable" );
                    }

                    break;
                }
                else if( hdr._relpath==_SOAP ) {
                    if( !_tun.is_connected() )
                        _tun.connect( _srvaddr.ptr() );

                    netAddress addr;
                    net.get_remote_address( &addr );

                    if( _tun.is_connected() )
                    {
                        _http.set_response( "200 OK" );

                        token service = hdr._relpath;
                        static substring soapgw("soap/");

                        service = service.get_after_substring(soapgw);

                        uint64 ssid;

                        _http.set_content_type("text/xml");
                        charstr svc = charstr(service);
                        e = _tun.process_local_soap_call(_http, svc, addr, ssid);
                        if( !e  &&  !hdr._bclose ) {
                            uint n = MAX_TIMEOUT / 2000;
                            while( n-->0 && !e && !_cmdin->is_exit_signalled() ) {
                                e = net.wait_read(2000);
                            }
                            if(!e)  continue;
                        }
                    }
                    else
                    {
                        send_error( "503 Service Unavailable" );
                    }

                    break;
                }
                else if( query.is_empty() )
                {
                    send_file();
                }
                else
                {
                    send_error( "400 Bad Request" );
                }
            }
            else
            {
                send_error( "501 Not Implemented" );
            }

            break;
        }

        net.lingering_close();

        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////
    opcd HttpServ::send_file()
    {
        char lc = _cwd.last_char();
        if( lc == '/' )
            _cwd.last_char(0);

        opcd e = _http.send_file( _cwd, get_mime_type(_cwd) );

        if( e == ersUNAVAILABLE )  return e;
        if( e == ersNOT_FOUND )
            send_error( "404 Not Found" );
        else if( e == ersINVALID_TYPE )
        {
            //it's a directory
            // if there was / originally, try index.html
            if( lc == '/' ) {
                _cwd.last_char(lc);
                _cwd << "index.html";
                return send_file();
            }

            //suggest request ending with /
            _http.set_optional_header("Location: ")
                << token(_cwd.ptr()+_cwdlen,_cwd.len()-_cwdlen) << "/\r\n";

            send_error( "301 Moved Permanently" );
        }
        else if( e == ersIGNORE )
        {
            send_error( "304 Not Modified" );
        }
        else if( e == ersDENIED )
        {
            send_error( "403 Forbidden" );
        }
        else if(e)
        {
            send_error( "500 Internal Error" );
        }

        return e;
    }

    ////////////////////////////////////////////////////////////////////////////////
    opcd HttpServ::send_error( const token& err )
    {
        token status = token(err).cut_left(' ');
        _cwd.resize(_cwdlen);
        _cwd << "/error/" << status << ".html";

        _http.set_response(err);
        opcd e = _http.send_file( _cwd, "text/html" );

        if(!e)
            return e;

        _http.set_content_type( "text/plain" );
        _http.flush();

        return 0;
    }

    ////////////////////////////////////////////////////////////////////////////////
    const charstr& HttpServ::get_mime_type( token fn ) const
    {
        fn.cut_left_back('.');

        return _mimereg.find_mime(fn);
    }



} //namespace coid
